/**
 *
 * Author: Junior Samarooo
 * Filename: read_serial_port.cpp
 * Project: TURFBOT
 *
 * A simple serial communications protocol to read from the GPS unit.
 * Its output in the terminal is the same as one would see from a serial
 * communications program such as "hyper terminal" in windows and "GtkTerm"
 * in linux.
 *
 * This program uses libserial for simplified serial port programming
 * in POSIX operating systems. See http://libserial.sourceforge.net/ or "README.txt"
 * for more information.
 *
 * The program outputs csv data to text files on the desktop(default location):
 *
 * NMEAdata.txt - contains the raw nmea sentences from the GPS unit as seen from the terminal
 *
 * GPSdata.txt - contains data in the following csv format:
 * 				 latitude(degrees), longitude(degrees), altitude(metres), geoid height(meters), # of satellites, speed(metres per second)
 *
 */

#include <iostream>
#include <SerialStream.h>		// simplified serial port programming library under POSIX operating systems
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fstream>
#include <iomanip>
using namespace std;
using namespace LibSerial ;		// namespace for <SerialStream.h>

#define serialPortLocation "/dev/ttyUSB0"								// Defines which serial port is being accessed
#define gpsDataLocation "/home/juniorsamaroo/Desktop/GPSdata.txt"		// location of the GPSdata.txt file
#define nmeaDataLocation "/home/juniorsamaroo/Desktop/NMEAdata.txt"		// location of the NMEAdata.txt file

int main(int argc, char** argv)
{
	/* Flag for when a valid GGA nmea sentence is obtained. */
	int ggaFix = 0;
	/*  Flag for when a valid RMC nmea sentence is obtained */
	int rmcFix = 0;
	/* Longitude in degrees */
	double longitude = 0 ;
	/* Latitude in degrees */
	double latitude = 0;
	/* Number of satellites being tracked */
	int satellites = 0;
	/* Altitude (metres) above sea level */
	double altitude = 0;
	/* Height of geoid (mean sea level) above WGS84 ellipsoid (metres) */
	double geoid = 0;
	/* UTC time XX:XX:XX */
	double time = 0;
	/* Speed over the ground in metres per second */
	double speed = 0;
	/* Stores GGA nmea sentences */
	string ggaNmea;
	/* Stores RMC nmea sentences */
	string rmcNmea;
	/* Output stream for gpsData.txt */
	ofstream gpsdata;
	/* Output stream for gpsData.txt */
	ofstream nmeaData;
	/* Serial port communication stream */
	SerialStream serial_port ;

    /**
     * Opens the serial port.
     */
    serial_port.Open( serialPortLocation ) ;
    if ( ! serial_port.good() )
    {
        cerr << "[" << __FILE__ << ":" << __LINE__ << "] "
                  << "Error: Could not open serial port."
                  << std::endl ;
        exit(1) ;
    }

    /**
     * Sets the baud rate of the serial port.
     *
     * The GPS unit communicates at 4800bps. So it is
     * set at this value.
     */
    serial_port.SetBaudRate( SerialStreamBuf::BAUD_4800 ) ;
    if ( ! serial_port.good() )
    {
        cerr << "Error: Could not set the baud rate." << std::endl ;
        exit(1) ;
    }

    /**
     * Sets the number of data bits.
     *
     * The GPS uses 8 data bits.
     */
    serial_port.SetCharSize( SerialStreamBuf::CHAR_SIZE_8 ) ;
    if ( ! serial_port.good() )
    {
        cerr << "Error: Could not set the character size." << std::endl ;
        exit(1) ;
    }

    /**
     * Sets the parity.
     *
     * The GPS uses zero parity.
     */
    serial_port.SetParity( SerialStreamBuf::PARITY_NONE ) ;
    if ( ! serial_port.good() )
    {
        cerr << "Error: Could not disable the parity." << std::endl ;
        exit(1) ;
    }

    /**
     * Sets the number of stop bits.
     *
     * The GPS uses 1 stop bit.
     */
    serial_port.SetNumOfStopBits( 1 ) ;
    if ( ! serial_port.good() )
    {
        cerr << "Error: Could not set the number of stop bits."
                  << std::endl ;
        exit(1) ;
    }

    /**
     * Sets the flow control.
     *
     * Hardware flow control is turned for the GPS.
     */
    serial_port.SetFlowControl( SerialStreamBuf::FLOW_CONTROL_HARD ) ;
    if ( ! serial_port.good() )
    {
        cerr << "Error: Could not use hardware flow control."
                  << std::endl ;
        exit(1) ;
    }

    /**
     * Read data from the serial port.
     *
     * Loop that keeps reading that from the serial port
     * and recording the data to text files
     * until it is explicitly stopped by the user.
     */
    while(1)
    {
    	string nmea;		// stores nmea sentences
    	string nmeaTemp;
    	serial_port >> nmea;// read from serial port
    	nmeaTemp = nmea;
        cout << nmea << endl;
        char *token;		//temporary tokenizer storage
        /**
         * Checks for GGA nmea sentences.
         * If found parses string appropriately.
         */
        if (nmea.find("$GPGGA") != string::npos)
        {
        	char *ggaOutput[14];
        	token = strtok (&nmea[0], ",");
        	int i = 0;
        	/**
        	 * Loop to tokenize GGA sentence.
        	 */
        	while(token != NULL)
        	{
        		ggaOutput[i] = token;
        		token = strtok (NULL, ",");
        		i++;
        	}
        	/**
        	 * Checks for a GPS fix.
        	 */
        	if (atoi(ggaOutput[6]) != 0)
        	{
        		ggaFix = 1;

        		/** Calculates latitude */
        		string temp = ggaOutput[2];
        		temp = temp.substr(0,2);
        		int degreePart = atoi(&temp[0]);
        		temp = ggaOutput[2];
        		temp = temp.substr(2);
        		double minutesPart = atof(&temp[0]);
        		latitude = degreePart + minutesPart/60;

        		/** Calculates longitude */
        		temp = ggaOutput[4];
        		temp = temp.substr(0,3);
        		double degreePart2 = atof(&temp[0]);
        		temp = ggaOutput[4];
        		temp = temp.substr(3);
        		double minutesPart2 = atof(&temp[0]);
        		longitude = degreePart2 + (minutesPart2/60);

        		time = atof(ggaOutput[1]);
				satellites = atoi(ggaOutput[7]);
				altitude = atof(ggaOutput[9]);
				geoid = atof(ggaOutput[11]);

				ggaNmea = nmeaTemp; // records gga sentence if valid.

        	}
        }

        /**
         * Checks for RMC nmea sentences.
         */
        if (nmea.find("$GPRMC") != string::npos)
        {
        	char *rmcOutput[11];
        	token = strtok (&nmea[0], ",");
        	int i = 0;
        	/**
        	 * Loop tokenizes RMC string.
        	 */
        	while(token != NULL)
        	{
        		rmcOutput[i] = token;
        		token = strtok (NULL, ",");
        		i++;
        	}

        	string fix = rmcOutput[2];
        	/**
        	 * Checks if RMC/GPS fix is valid.
        	 */
        	if (fix.compare("A") == 0)
        	{
        		rmcFix = 1;
        		speed = (atof(rmcOutput[7]))*(0.514444444);

        		rmcNmea = nmeaTemp; // records rmc sentence if valid.

        	}
        }

        /**
         * If valid RMC and GGA sentences are found, records data
         * to appropriate text file.
         *
         * gpsData.txt records data up to 9 decimal point precision.
         *
         */
        if ((ggaFix == 1) && (rmcFix == 1))
        {
        	gpsdata.open(gpsDataLocation, ios::out | ios::app);
        	nmeaData.open(nmeaDataLocation, ios::out | ios::app);
        	gpsdata << setprecision(9) << latitude << "," << longitude << "," << altitude << "," << geoid << "," << satellites << "," << speed <<  endl;
        	nmeaData << ggaNmea << endl;	// Writes data to file
        	nmeaData << rmcNmea << endl;	//
        	gpsdata.close();
        	nmeaData.close();
        	ggaFix = 0;
        	rmcFix = 0;
        }

    }
    cerr << endl ;
    return EXIT_SUCCESS ;
}
